#include "config.h"

#include "lich_api.h"
#include "lichbd.h"
#include "utils.h"

typedef enum {
        OP_NULL,
        OP_SYSSTAT,
        OP_CREATE,
        OP_LISTNODE,
        OP_STAT,
        OP_SETMETA,
        OP_DROPMETA,
        OP_ADDNODE,
        OP_DROPNODE,
        OP_CASTOFF,
        OP_SETVIP,
        OP_UNSETVIP,
        OP_MOVEMETA,
        OP_SETARBITOR,
        OP_CONFIGDUMP,
        OP_HOSTSDUMP,
        
        OP_SETVARIABLE,
        OP_FETCHVARIABLE,
        OP_CREATETIME,

        OP_POOLIST,
        OP_POOLCREATE,
        OP_POOLREMOVE,
        OP_POOLDROP,
        OP_POOLDUMP,
        OP_SETCACHEPOLICY,
        OP_GETCACHEPOLICY,
} admin_op_t;

typedef enum {
        CACHE_INTELCAS = 1,
        CACHE_BCACHE,
        CACHE_DMCACHE,
}cache_type_t;

static int verbose = 0;
static int __load__ = 0;

static void usage()
{
        fprintf(stderr,
                "lich.admin (version 1.0.0.1)\n"
                "Usage: lich.admin [OPTIONS] <cmd>\n"
                "lich.admin --sysstat\n"
                "lich.admin --configdump\n"
                "lich.admin --hostsdump\n"
                "lich.admin --setvip\n"
                "lich.admin --unsetvip\n"
                "lich.admin --create node1\n"
                "\n"
                "lich.admin --addnode node3 node4 ... nodeX\n"
                "lich.admin --dropnode node3 node4 ... nodeX\n"
                "lich.admin --listnode [-v, --verbose] [--load]\n"
                "lich.admin --stat nodeX\n"
                "\n"
                "lich.admin --setmeta nodeX\n"
                "lich.admin --dropmeta nodeX\n"
                "\n"
                "lich.admin --castoff <nodeX | nodeX/0>\n"
                "\n"
                "lich.admin --setvariable <key> <value> [-n host]\n"
                "lich.admin --fetchvariable <key> [-n host]\n"
                "lich.admin --createtime\n"
                "\n"
                "lich.admin --poolist\n"
                "lich.admin --poolcreate <pool>\n"
                "lich.admin --poolremove <pool>\n"
                "lich.admin --pooldrop <pool>\n"
                "lich.admin --pooldump <pool>\n"
                "lich.admin --setcachepolicy <key> <value> --pool <pool>\n"
                "lich.admin --getcachepolicy <key> --pool <pool>\n"
                );
}

static char *__cache_type_str(int cache_type)
{
        switch(cache_type) {
                case CACHE_BCACHE:
                        return "bcache";
                case CACHE_INTELCAS:
                        return "intelcas";
                case CACHE_DMCACHE:
                        return "dmcache";
                default:
                        return "invalid";
        }
}

static int __admin_stat(const char *name)
{
        int ret, retry = 0, i;
        char buf[MAX_BUF_LEN];
        nodeinfo_t info;
        nodedfree_t *dfree;

        YASSERT(name);

retry:
        ret = node_getinfo(&info, name, buf);
        if (unlikely(ret)) {
                if (ret == ENONET || ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        ret = ymalloc((void **)&dfree, sizeof(*dfree));
        if (unlikely(ret))
                GOTO(err_ret, ret);

        dfinfo_decode(info.dfinfo, strlen(info.dfinfo) + 1, dfree);

        /*
        if ((LLU)info->stat->total <= (LLU)cdsconf.disk_keep 
                || ((LLU)info->stat->used + (LLU)cdsconf.disk_keep) >= (LLU)info->stat->total) {
                info->stat->used = info->stat->total;
        } else {
                info->stat->used += (uint64_t)cdsconf.disk_keep;
        }
        */

        if (info.stat->status & __NODE_STAT_DELETING__) {
                printf("cluster:%s\n"
                         "hostname:%s\n"
                         "nid:"NID_FORMAT"\n"
                         "writeable:%s\n"
                         "ready:%s\n"
                         "deleting:%s\n"
                         "load:%llu\n"
                         "admin:%s\n"
                         "status:%s\n"
                         "home:%s\n"
                         "uptime:%d\n",
                         info.clustername,
                         info.nodename,
                         NID_ARG(&info.stat->nid),
                         info.stat->status & __NODE_STAT_WRITEABLE__ ? "True" : "False",
                         info.stat->status & __NODE_STAT_READY__ ? "True" : "False",
                         info.stat->status & __NODE_STAT_DELETING__? "True" : "False",
                         (LLU)info.stat->load,
                         info.admin,
                         info.status,
                         info.home,
                         *info.uptime);
        } else {
                printf("cluster:%s\n"
                         "hostname:%s\n"
                         "nid:"NID_FORMAT"\n"
                         "writeable:%s\n"
                         "ready:%s\n"
                         "load:%llu\n"
                         "admin:%s\n"
                         "status:%s\n"
                         "home:%s\n"
                         "uptime:%d\n",
                         info.clustername,
                         info.nodename,
                         NID_ARG(&info.stat->nid),
                         info.stat->status & __NODE_STAT_WRITEABLE__ ? "True" : "False",
                         info.stat->status & __NODE_STAT_READY__ ? "True" : "False",
                         (LLU)info.stat->load,
                         info.admin,
                         info.status,
                         info.home,
                         *info.uptime);
        }

        printf("pool_count:%d\n", dfree->pool_count);
        for (i = 0; i < dfree->pool_count; i++) {
                printf("%s.total:%ld\n", dfree->pool_stat[i].name, dfree->pool_stat[i].total);
                printf("%s.used:%ld\n", dfree->pool_stat[i].name, dfree->pool_stat[i].used);
        }

        yfree((void **)&dfree);

        return 0;
err_ret:
        return ret;
}

static int __admin_listnode()
{
        int ret, buflen, done = 0;
        char buf[MAX_BUF_LEN], tmp[MAX_BUF_LEN];
        uint64_t offset = 0, offset2 = 0;
        struct dirent *de;
        nodeinfo_t info;
        uuid_t _uuid;
        char uuid[UUID_LEN] = {};

        uuid_generate(_uuid);
        uuid_unparse(_uuid, uuid);

        ret = cluster_listnode_open(uuid);
        if (ret)
                GOTO(err_ret, ret);

        while (done == 0) {
                memset(buf, 0, sizeof(buf));
                ret = cluster_listnode(buf, &buflen, uuid, offset);
                if (unlikely(ret)) {
                        GOTO(err_close, ret);
                }

                if (buflen == 0) {
                        break;
                }

                offset2 = 0;
                dir_for_each(buf, buflen, de, offset2) {
                        if (strlen(de->d_name) == 0) {
                                done = 1;
                                break;
                        } else if (buflen - offset2 < sizeof(*de) + MAX_NAME_LEN)
                                break;

			offset += de->d_reclen;

                        ret = node_getinfo(&info, de->d_name, tmp);
                        if (unlikely(ret)) {
#if 0
                                if (verbose) {
                                        printf("\x1b[1;31m%s:stopped\x1b[0m\n", de->d_name);
                                } else
                                        printf("\x1b[1;31m%s\x1b[0m\n", de->d_name);
#endif
                                if (verbose) {
                                        printf("%s:stopped\n", de->d_name);
                                } else
                                        printf("%s\n", de->d_name);
                        } else {
                                if (verbose || __load__) {
                                        if (verbose) {
                                                if (info.stat->status & __NODE_STAT_DELETING__)
                                                        printf("%s:%s,%s\n", de->d_name, info.status, "deleting");
                                                else
                                                        printf("%s:%s\n", de->d_name, info.status);
                                        } else {
                                                printf("%s:%llu\n", de->d_name, (LLU)info.stat->load);
                                        }
                                } else
                                        printf("%s\n", de->d_name);
                        }
                }
        }

        cluster_listnode_close(uuid);
        return 0;
err_close:
        cluster_listnode_close(uuid);
err_ret:
        return ret;
}

static int __admin_dropnode(const char **list, int count)
{
        int ret, retry = 0;

retry:
        ret = network_connect_master();
        if (unlikely(ret)) {
                if (ret == ENONET || ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        ret = cluster_dropnode(list, count);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

static int __admin_castoff(const char *name)
{
        int ret, disk;
        char *c;
        char host[MAX_NAME_LEN];

        c = strchr(name, '/');
        if (c) {
                *c = '\0';
                disk = atoi(c+1);
        } else {
                disk = DISK_ALL_IDX;
        }

        ret = net_gethostname(host, MAX_MSG_SIZE);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        if (strcmp(name, host) != 0) {
                DERROR("the local host is: %s, need exec on host: %s\n", host, name);
                ret = 1;
                GOTO(err_ret, ret);
        }

        return node_castoff(name, disk);
err_ret:
        return ret;
}

static int __admin_setvip()
{
        int ret;

        ret = netvip_setvip();
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __admin_unsetvip()
{
        int ret;

        ret = netvip_destroy();
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __admin_sysstat(int force)
{
        int ret, retry = 0;
        lich_stat_t stat;

retry:
        ret = network_connect_master();
        if (unlikely(ret)) {
                if (ret == ENONET || ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        ret = dispatch_sysstat(&stat, force);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        printf("site:%d,%d\n"
               "rack:%d,%d\n"
               "node:%d,%d\n"
               "disk:%d,%d\n", 
               stat.site_online, stat.site_total,
               stat.rack_online, stat.rack_total,
               stat.node_online, stat.node_total,
               stat.disk_online, stat.disk_total);

        return 0;
err_ret:
        return ret;
}

static int __lich_config_dump()
{
        int ret, i;
        char hostname[MAX_NAME_LEN];

        ret = conf_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        dbg_sub_init();

        ret = net_gethostname(hostname, MAX_NAME_LEN);
        if (unlikely(ret)) {
                if (ret == ECONNREFUSED)
                        strcpy(hostname, "N/A");
                else
                        GOTO(err_ret, ret);
        }

        printf("globals.clustername:%s\n"
               "globals.hostname:%s\n"
               "globals.home:%s\n"
               "globals.rpc_timeout:%d\n"
               "globals.lichbd_root:%s\n"
               "globals.nbd_root:%s\n"
               "globals.sheepdog_root:%s\n"
               "globals.localize:%s\n"
               "globals.writeback:%s\n"
               "globals.priority:%s\n"
               "globals.multipath:%s\n"
               "globals.wmem_max:%llu\n"
               "globals.rmem_max:%llu\n"
               "globals.replica_max:%u\n"
               "globals.crontab:%s\n"
               "globals.nohosts:%s\n"
               "globals.cgroup:%s\n"
               "globals.cleanlogcore:%s\n"
               "globals.coredump:%s\n"
               "globals.log_max_bytes:%llu\n"
               "globals.testing:%s\n"
               "globals.default_protocol:%s\n"
               "globals.storage_area_max_node:%d\n"
               "globals.chunk_rep:%d\n"
               "globals.chunk_size:%d\n"
               "globals.polling_core:%s\n"
               "globals.memcache_seg:%d\n"
               "globals.memcache_count:%d\n"
               "globals.cache_type:%s\n"
               "globals.cache_enable:%s\n"
               "globals.spdk:%s\n"
               "globals.kv_engine:%s\n"
               "metadata.meta:%d\n"
               "iscsi.iqn:%s\n"
               "iscsi.port:%d\n"
               "iscsi.timeout:%d\n"
               "ucarp.vip:%s\n"
               "nfs.nfsd:%d\n"
               "cdsconf.disk_keep:%llu\n"
               "redis_group:%u\n",
               gloconf.cluster_name,
               hostname,
               gloconf.home,
               gloconf.rpc_timeout,
               gloconf.lichbd_root,
               gloconf.nbd_root,
               gloconf.sheepdog_root,
               gloconf.localize ? "on" : "off",
               gloconf.writeback ? "on" : "off",
               gloconf.priority == -1 ? "default" : gloconf.priority ? "on" : "off",
               gloconf.multipath ? "on" : "off",
               (LLU)gloconf.wmem_max,
               (LLU)gloconf.rmem_max,
               LICH_REPLICA_MAX,
               gloconf.crontab ? "on" : "off",
               gloconf.nohosts ? "on" : "off",
               gloconf.cgroup ? "on" : "off",
               gloconf.cleanlogcore ? "on" : "off",
               gloconf.coredump ? "on" : "off",
               (LLU)gloconf.log_max_bytes,
               gloconf.testing ? "on" : "off",
               gloconf.default_protocol,
               gloconf.storage_area_max_node,
               gloconf.chunk_rep,
               (int)LICH_CHUNK_SPLIT,
               gloconf.polling_core,
               gloconf.memcache_seg,
               gloconf.memcache_count,
               __cache_type_str(gloconf.cache_type),
               gloconf.cache_enable ? "on" : "off",
               gloconf.spdk ? "on" : "off",
               gloconf.kv_redis ? "redis" : "sqlite",
               mdsconf.meta,
               sanconf.iqn,
               sanconf.iscsi_port,
               sanconf.iscsi_timeout,
               sanconf.ucarp_vip,
               gloconf.nfsd,
               (LLU)cdsconf.disk_keep,
               REDIS_GROUP);

        printf("iscsi.vip: ");
        for (i = 0; i < sanconf.iscsi_vip.vip_count; i++) {
                printf("%s", sanconf.iscsi_vip.vip[i]);
                if (i + 1 != sanconf.iscsi_vip.vip_count)
                        printf(",");
                else
                        printf("\n");
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_hosts_dump()
{
        int ret;

        ret = conf_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = hosts_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        hosts_dump();

        return 0;
err_ret:
        return ret;
}

static int __get_hostname(const char *src, char *hostname)
{
        int ret;
        
        if (NULL == src) {
                ret = net_gethostname(hostname, MAX_NAME_LEN);
                if (unlikely(ret)) {
                        GOTO(err_ret, ret);
                }
        } else {
                strcpy(hostname, src);
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_set_variable(const char *key, const char *value, const char *host)
{
        int ret;
        char hostname[MAX_NAME_LEN];
        
        if (0 == strlen(key) || 0 == strlen(value)) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }
        
        ret = __get_hostname(host, hostname);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

	ret = node_set_variable(hostname, key, value);
	if (unlikely(ret)) {
                fprintf(stderr, "filed to set variable %s\t%s\n", key, strerror(ret));
                GOTO(err_ret, ret);
	}

        fprintf(stderr, "[%s : %s]\n",key , value);

	return 0;
err_ret:
	return ret;
}

static int __lich_fetch_variable(const char *key, const char *host)
{
        int ret;
        char hostname[MAX_NAME_LEN], value[MAX_NAME_LEN];

        if (0 == strlen(key)) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }
        
        ret = __get_hostname(host, hostname);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }
                
	ret = node_fetch_variable(hostname, key, value);
	if (unlikely(ret)) {
                fprintf(stderr, "filed to fetch variable %s\t%s\n", key, strerror(ret));
                GOTO(err_ret, ret);
	}

        fprintf(stderr, "[%s : %s]\n",key , value);

        return 0;
err_ret:
        return ret;
}

static int __lich_set_cachepolicy(const char *key, const char *value, const char *pool)
{
        int ret;
        char cmd[MAX_INFO_LEN] = {0};
        FILE *fp = NULL;

        if(key == NULL || value == NULL || pool == NULL) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        snprintf(cmd, MAX_INFO_LEN, "etcdctl set /lich4/cachepolicy/%s/%s %s", pool, key, value);
        fp = popen(cmd, "r");
        if (fp == NULL) {
                ret = EPERM;
                GOTO(err_ret, ret);
        }

        if(fp) {
                ret = pclose(fp);
                if(ret < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                } else if(WIFEXITED(ret)) {
                        if(WEXITSTATUS(ret) != 0) {
                                DERROR("cmd [%s], child return code[%d]\n",
                                                cmd, WEXITSTATUS(ret));
                                GOTO(err_ret, ret);
                        }
                } else {
                        ret = EINVAL;
                        GOTO(err_ret, ret);
                }
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_get_cachepolicy(const char *pool, const char *key, char *value)
{
        int ret;
        char cmd[MAX_INFO_LEN] = {0};
        FILE *fp = NULL;

        if(pool == NULL) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

        snprintf(cmd, MAX_INFO_LEN, "etcdctl get /lich4/cachepolicy/%s/%s", pool, key);
        fp = popen(cmd, "r");
        if (fp == NULL) {
                ret = EPERM;
                GOTO(err_ret, ret);
        }

        fgets(value, MAX_LINE_LEN, fp);

        if(fp) {
                ret = pclose(fp);
                if(ret < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                } else if(WIFEXITED(ret)) {
                        if(WEXITSTATUS(ret) != 0) {
                                DERROR("cmd [%s], child return code[%d]\n",
                                                cmd, WEXITSTATUS(ret));
                                GOTO(err_ret, ret);
                        }
                } else {
                        ret = EINVAL;
                        GOTO(err_ret, ret);
                }
        }

        return 0;
err_ret:
        return ret;
}


static int __lich_get_createtime(int verbose)
{
        int ret;
        time_t createtime;
        struct tm *tm_limit, t;
        char buf[MAXSIZE+1];

        ret = network_connect_master();
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        ret = system_get_createtime(&createtime);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        if (!verbose) {
                tm_limit = localtime_safe(&createtime, &t);
                strftime(buf, MAXSIZE, "%F %T", tm_limit);
                printf("ctime:%s\n", buf);
        } else {
                printf("ctime:%ld\n", createtime);
        }

        return 0;
err_ret:
        return ret;
}

static int __lich_pool_list()
{
        return utils_pool_list(0);
}

static int __lich_pool_create(const char *pool)
{
        return utils_pool_create(pool);
}

static int __lich_pool_remove(const char *pool)
{
        return utils_pool_remove(pool);
}

static int __lich_pool_drop(const char *pool)
{
        return utils_pool_drop(pool);
}

static int __lich_pool_dump(const char *pool)
{
        int ret, retry = 0;
retry:
        ret = network_connect_master();
        if (unlikely(ret)) {
                if (ret == ENONET || ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 50, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        ret = dispatch_pooldump(pool);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int main(int argc, char *argv[])
{
        int ret, op = OP_NULL, force = 0, noinit = 0;
        char c_opt;
        char *name = NULL, *key = NULL, *value = NULL, *host = NULL, *pool = NULL;
        char cachepolicy[MAX_INFO_LEN] = {0};

        dbg_info(0);

        (void) verbose;

        while (srv_running) {
                int option_index = 0;

                static struct option long_options[] = {
                        { "setmeta", required_argument, 0, 0},
                        { "dropmeta", required_argument, 0, 0},
                        { "load", no_argument, 0, 0},
                        { "force", no_argument, 0, 0},
                        { "create", required_argument, 0, 0},
                        { "sysstat", no_argument, 0, 0},
                        { "configdump", no_argument, 0, 0},
                        { "hostsdump", no_argument, 0, 0},
                        { "castoff", required_argument, 0, 0},
                        { "setvip", no_argument, 0, 0},
                        { "unsetvip", no_argument, 0, 0},
                        { "setvariable", required_argument, 0, 0},
                        { "fetchvariable", required_argument, 0, 0},
                        { "createtime", no_argument, 0, 0},
                        { "poolist", no_argument, 0, 0},
                        { "poolcreate", required_argument, 0, 0},
                        { "poolremove", required_argument, 0, 0},
                        { "pooldrop", required_argument, 0, 0},
                        { "pooldump", required_argument, 0, 0},
                        { "setcachepolicy", required_argument, 0, 0},
                        { "getcachepolicy", required_argument, 0, 0},
                        { "pool", required_argument, 0, 'p'},
                        { "createcluster", required_argument, 0, 'c'},
                        { "listnode", no_argument, 0, 'l'},
                        { "stat", required_argument, 0, 's'},
                        { "addnode", no_argument, 0, 'a'},
                        { "dropnode", no_argument, 0, 'd'},
                        { "host", required_argument, 0, 'n'},
                        //{ "movemeta", required_argument, 0, 'm'},
                        { "verbose", 0, 0, 'v' },
                        { "help",    0, 0, 'h' },
                        { 0, 0, 0, 0 },
                };

                c_opt = getopt_long(argc, argv, "n:cls:advh", long_options, &option_index);
                if (c_opt == -1)
                        break;

                switch (c_opt) {
                case 0:
                        switch (option_index) {
                        case 0:
                                DBUG("setmeta %s\n", optarg);
                                op = OP_SETMETA;
                                name = optarg;
                                break;
                        case 1:
                                DBUG("dropmeta %s\n", optarg);
                                op = OP_DROPMETA;
                                name = optarg;
                                break;
                        case 2:
                                __load__ = 1;
                                break;
                        case 3:
                                force = 1;
                                break;
                        case 4:
                                DBUG("create cluster\n");
                                op = OP_CREATE;
                                name = optarg;
                                break;
                        case 5:
                                op = OP_SYSSTAT;
                                break;
                        case 6:
                                op = OP_CONFIGDUMP;
                                noinit = 1;
                                break;
                        case 7:
                                op = OP_HOSTSDUMP;
                                noinit = 1;
                                break;
                        case 8:
                                op = OP_CASTOFF;
                                name = optarg;
                                break;
                        case 9:
                                op = OP_SETVIP;
                                break;
                        case 10:
                                op = OP_UNSETVIP;
                                break;
                        case 11:
                                op = OP_SETVARIABLE;
                                key = optarg;
                                value = argv[3];
                                DBUG("set variable\n");
                                break;
                        case 12:
                                op = OP_FETCHVARIABLE;
                                key = optarg;
                                DBUG("fetch variable\n");
                                break;
                        case 13:
                                op = OP_CREATETIME;
                                break;
                        case 14:
                                op = OP_POOLIST;
                                break;
                        case 15:
                                op = OP_POOLCREATE;
                                name = optarg;
                                break;
                        case 16:
                                op = OP_POOLREMOVE;
                                name = optarg;
                                break;
                        case 17:
                                op = OP_POOLDROP;
                                name = optarg;
                                break;
                        case 18:
                                op = OP_POOLDUMP;
                                name = optarg;
                                break;
                        case 19:
                                op = OP_SETCACHEPOLICY;
                                key = optarg;
                                value = argv[3];
                                DBUG("set cache policy\n");
                                break;
                        case 20:
                                op = OP_GETCACHEPOLICY;
                                key = optarg;
                                break;
                        default:
                                fprintf(stderr, "Hoops, wrong op got xxxx !\n");
                        }

                        break;
                case 'k':
                        key = optarg;
                        break;
                case 'u':
                        value = optarg;
                        break;
                case 'c':
                        DBUG("create cluster\n");
                        op = OP_CREATE;
                        name = optarg;
                        break;
                case 'l':
                        DBUG("node list\n");
                        op = OP_LISTNODE;
                        break;
                case 's':
                        DBUG("stat %s\n", optarg);
                        op = OP_STAT;
                        name = optarg;
                        break;
                case 'a':
                        DBUG("addnode %s\n", optarg);
                        op = OP_ADDNODE;
                        break;
                case 'd':
                        DBUG("dropnode %s\n", optarg);
                        op = OP_DROPNODE;
                        break;
                case 'n':
                        host = optarg;
                        break;
                case 'p':
                        pool = optarg;
                        break;
                case 'v':
                        verbose = 1;
                        break;
                case 'h':
                        usage();
                        EXIT(0);
                default:
                        usage();
                        EXIT(EINVAL);
                }
        }

#if 1
        if (argc == 1) {
                usage();
                exit(EINVAL);
        }
#endif

        if (noinit == 0) {
                if (op == OP_POOLREMOVE) {
                        ret = lichbd_init("");
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else {
                        ret = env_init_simple("lich.admin");
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                }
        }

        if (op != OP_NULL) {
                switch (op) {
                case OP_CREATE:
                        UNIMPLEMENTED(__DUMP__);

                case OP_STAT:
                        ret = __admin_stat(name);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_SETMETA:
                        UNIMPLEMENTED(__DUMP__);

                case OP_DROPMETA:
                        UNIMPLEMENTED(__NULL__);

                        break;
                case OP_LISTNODE:
                        ret = __admin_listnode();
                        if (unlikely(ret))
                                GOTO(err_ret, ret);

                        break;
                case OP_ADDNODE:
                        UNIMPLEMENTED(__NULL__);
                        break;
                case OP_DROPNODE:
                        ret = __admin_dropnode((void *)&argv[2], argc - 2);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_CASTOFF:
                        ret = __admin_castoff(name);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_SETVIP:
                        ret = __admin_setvip();
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_UNSETVIP:
                        ret = __admin_unsetvip();
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_SETVARIABLE:
                        if (!key || !value ) {
                                fprintf(stderr, "<key> <value> must be specified!\n");
                                ret = EINVAL;
                                GOTO(err_ret, ret);
                        }
                        ret = __lich_set_variable(key, value, host);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_FETCHVARIABLE:
                        if (!key) {
                                fprintf(stderr, "<key> must be specified!\n");
                                ret = EINVAL;
                                GOTO(err_ret, ret);
                        } 
                        ret = __lich_fetch_variable(key, host);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_CREATETIME:
                        ret = __lich_get_createtime(verbose);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_SYSSTAT:
                        ret = __admin_sysstat(force);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_CONFIGDUMP:
                        ret = __lich_config_dump();
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_HOSTSDUMP:
                        ret = __lich_hosts_dump();
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_POOLIST:
                        ret = __lich_pool_list();
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_POOLCREATE:
                        ret = __lich_pool_create(name);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_POOLREMOVE:
                        ret = __lich_pool_remove(name);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_POOLDROP:
                        ret = __lich_pool_drop(name);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_POOLDUMP:
                        ret = __lich_pool_dump(name);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                        break;
                case OP_SETCACHEPOLICY:
                        ret = __lich_set_cachepolicy(key, value, pool);
                        if (unlikely(ret)) {
                                ret = __lich_get_cachepolicy(pool, key, cachepolicy);
                                if(unlikely(ret)) {
                                        GOTO(err_ret, ret);
                                }

                                if(strcmp(cachepolicy, value)) {
                                        GOTO(err_ret, ret);
                                }
                        }
                        break;
                case OP_GETCACHEPOLICY:
                        ret = __lich_get_cachepolicy(pool, key, cachepolicy);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);

                        fprintf(stdout, "%s", cachepolicy);

                        break;
                default:
                        usage();
                        EXIT(EINVAL);
                }
        }

        return 0;
err_ret:
        exit(ret);
}
